/*
 * Copyright Hundsun Technologies Inc. All Rights Reserved.
 */

package funcs

import (
	"context"
	"errors"
	"time"

	"hundsun.com/hsl/hschain/common/log/logging"
)

// GoroutineErrorWrapper goroutine错误包装
type GoroutineErrorWrapper struct {
	Error        error
	GoroutineTag string
}

//ErrHandlerCancellation ...
var ErrHandlerCancellation = errors.New("handler ")

//Handler ...
type Handler func() (interface{}, error)

//TryConfig 重试配置
type TryConfig struct {
	OperationName string

	Log logging.Log

	//RetryInterval 重试间隔时间（ms）
	RetryInterval int

	//LogInterval 日志打印周期时间（ms）
	LogInterval int64
}

// TryUntilSuccessOrContextDone 尝试指定操作直到成功或者context已经结束。
// @param ctx 上下文，用于取消重试
// @param handler　业务处理函数
// @param tryConfig 重试配置
// @return handlerResult 业务处理函数的第一个返回结果
// @return ok 是否执行成功
func TryUntilSuccessOrContextDone(ctx context.Context, handler Handler, tryConfig TryConfig) (handlerResult interface{}, ok bool) {
	var err error
	handlerResult, err = handler()
	retryCounter := 1
	var logTime int64
	for err != nil {
		if err == ErrHandlerCancellation {
			break
		}
		select {
		case <-time.After(time.Duration(tryConfig.RetryInterval) * time.Millisecond):
			if time.Now().UnixNano()-logTime >= tryConfig.LogInterval*int64(time.Millisecond) {
				tryConfig.Log.Errorf("operation:%s, tryTimes:%d, error:%s", tryConfig.OperationName, retryCounter, err.Error())
				logTime = time.Now().UnixNano()
			}
			handlerResult, err = handler()
			retryCounter++
		case <-ctx.Done():
			tryConfig.Log.Warnf("operation '%s' not success until ctx was done", tryConfig.OperationName)
			return handlerResult, err == nil
		}
	}
	return handlerResult, err == nil
}
